package com.mrjy.aiui.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 讯飞aiui接口调用工具类
 * @author csshui
 * @date 20180412
 *
 */
public class IatUtils {
	private static Logger logger = LoggerFactory.getLogger(IatUtils.class);
	private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",
			"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
	//讯飞开放平台注册申请应用的应用ID(APPID)
	private final static String xAppid = "5ac4a800" ;
	//ApiKey创建应用时自动生成
	private final static String apiKey = "7ac29f2c4654422aa9b976646b7a7d7a";
	//WebAPI 接口/文本语义
	private final static String TEXT_SEMANTIC_URL = "http://api.xfyun.cn/v1/aiui/v1/text_semantic" ;
	//WebAPI 接口/语音识别
	private final static String IAT_URL = "http://api.xfyun.cn/v1/aiui/v1/iat" ;
	//WebAPI 接口/语音语义
	private final static String VOICE_SEMANTIC_URL = "http://api.xfyun.cn/v1/aiui/v1/voice_semantic" ;
	
	//科大讯飞IAT平台的命名空间	
	public final static String NAME_SPACE = "MRJY" ;
	//科大讯飞iat返回时的service 问答库
	public final static String OPEN_QA = "openQA" ;
	//科大讯飞iat返回时的service 自定义技能 （家庭教育智能咨询）
	public final static String MRJY_JINENG_NAME = "MRJY.365jzkt" ;
	/**
	 * 文本语义接口调用
	 * @param userId (必填)用户 id，适用于多轮对话。示例：user_0001
	 * @param scene  情景模式 示例：	main，默认为“main”
	 * @param txt	  文本 示例：今天星期几 base64编码：5LuK5aSp5pif5pyf5Yeg
	 * @return
	 */
	public static String textSemantic(String userId ,String scene , String txt){
		if(StringUtils.isBlank(scene)){
			scene = "main" ;
		}
		String xCurTime = String.valueOf(System.currentTimeMillis() / 1000l) ;
		String xParam = getBase64("{\"scene\":\""+ scene +"\", \"userid\":\""+ userId +"\"}");
		String data = "text=" + getBase64(txt) ;
		String xCheckSum = md5Encode(apiKey + xCurTime + xParam + data) ;
		String result = IatUtils.postAIUI(IatUtils.TEXT_SEMANTIC_URL ,xCurTime, xCheckSum , xParam , data) ;
		return result ;
	}
	/**
	 * 接口/语音识别 根据输入语音进行识别，然后返回识别出的文本信息
	 * @param scene 场景 默认为main
	 * @param auf   音频格式 默认为 16k ，可填8k，16k
	 * @param aue	音频编码 默认为raw ，可填raw、speex、speex-wb
	 * @param audio_path 音频上传完保存的路径
	 * @return
	 */
	public static String iat(String scene,String auf , String aue, String audio_path){
		if(StringUtils.isBlank(scene)){
			scene = "main" ;
		}
		if(StringUtils.isBlank(auf)){
			auf = "16k" ;
		}
		if(StringUtils.isBlank(aue)){
			aue = "raw" ;
		}
		String xCurTime = String.valueOf(System.currentTimeMillis() / 1000l) ;
		String xParam = getBase64("{\"auf\":\"" + auf + "\",\"aue\":\"" + aue +"\",\"scene\":\"" + scene +"\"}");
		//音频文件
		File file = new File(audio_path);
		String data = null;
		try {
			InputStream is = new FileInputStream(file);
			byte[] bytes = IOUtils.toByteArray(is);
			data = Base64.encodeBase64String(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		}
		data = "data=" + data;
		String xCheckSum = md5Encode(apiKey + xCurTime + xParam + data) ;
		String result = IatUtils.postAIUI(IatUtils.IAT_URL ,xCurTime, xCheckSum , xParam , data) ;
		return result ;
	}
	/**
	 * 接口/语音语义  根据输入的一段语音，进行转义，然后会返回一个回答，如果不匹配，则和语义识别一样
	 * @param userId 用户id
	 * @param scene 场景 默认为main
	 * @param auf	音频格式 默认为 16k ，可填8k，16k
	 * @param aue	音频编码 默认为raw ，可填raw、speex、speex-wb
	 * @param audio_path	音频上传完保存的路径
	 * @return
	 */
	public static String voiceSemantic(String userId , String scene,String auf , String aue, String audio_path){
		if(StringUtils.isBlank(scene)){
			scene = "main" ;
		}
		if(StringUtils.isBlank(auf)){
			auf = "16k" ;
		}
		if(StringUtils.isBlank(aue)){
			aue = "raw" ;
		}
		String xCurTime = String.valueOf(System.currentTimeMillis() / 1000l) ;
		String xParam = getBase64("{\"userid\":\"" + userId + "\",\"auf\":\"" + auf + "\",\"aue\":\"" + aue +"\",\"scene\":\"" + scene +"\"}");
		//音频文件
		File file = new File(audio_path);
		String data = null;
		try {
			InputStream is = new FileInputStream(file);
			byte[] bytes = IOUtils.toByteArray(is);
			data = Base64.encodeBase64String(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		}
		data = "data=" + data;
		String xCheckSum = md5Encode(apiKey + xCurTime + xParam + data) ;
		String result = IatUtils.postAIUI(IatUtils.VOICE_SEMANTIC_URL ,xCurTime, xCheckSum , xParam , data) ;
		return result ;
	}
	
	/**
	 * 讯飞aiui请求方法
	 * @param url 接口url
	 * @param xCurTime 当前UTC时间戳，从1970年1月1日0点0 分0 秒开始到现在的秒数(String)
	 * @param xCheckSum 	MD5(ApiKey + CurTime + Param + http_body),四个参数拼接的字符串，进行MD5哈希计算。其中http_body 即为请求服务的实际服务HTTP body
	 * @param xParam 标准JSON格式参数需把参数组装json对象，然后对json进行base64编码，例如：json: {"scene":"main", "userid":"user_0001"} 
	 * 			     base64编码： eyJzY2VuZSI6Im1haW4iLCAidXNlcmlkIjoidXNlcl8wMDAxIn0=
	 * @return
	 */
	public static String postAIUI(String url, String xCurTime , String xCheckSum  , String xParam,String data){
		PrintWriter out = null;
		BufferedReader in = null;
		StringBuffer buffer = new StringBuffer() ;
		try {
			URL realUrl = new URL(url);
			// 打开和URL之间的连接
			HttpURLConnection conn = (HttpURLConnection) realUrl
					.openConnection();
			conn.setReadTimeout(2000);
			conn.setConnectTimeout(1000);
			conn.setRequestMethod("POST");
			// 发送POST请求必须设置如下两行
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setRequestProperty("X-Appid", xAppid);
			conn.setRequestProperty("X-CurTime", xCurTime);
			conn.setRequestProperty("X-Param", xParam);
			conn.setRequestProperty("X-CheckSum", xCheckSum);
			conn.setRequestProperty("Connection", "keep-alive");
			conn.setRequestProperty("Content-type",
					"application/x-www-form-urlencoded; charset=utf-8");
			// 获取URLConnection对象对应的输出流
			out = new PrintWriter(conn.getOutputStream());
			// 发送请求参数
			out.print(data);
			// flush输出流的缓冲
			out.flush();
			// 定义BufferedReader输入流来读取URL的响应
			// 将返回的输入流转换成字符串
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(
					inputStream, "utf-8");
			in = new BufferedReader(inputStreamReader);
			String line;
			while ((line = in.readLine()) != null) {
				buffer.append(line) ;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return buffer.toString() ;
	}
	
	/**
	 * Base64加密
	 * @author jlchen4
	 * @date 2017年9月16日 下午3:45:30
	 * @param str	加密字符串
	 * @return
	 */
	public static String getBase64(String str) {
		if (str == null || "".equals(str)) {
			return "";
		}
		try {
			byte[] encodeBase64 = Base64.encodeBase64(str.getBytes("UTF-8"));
			str = new String(encodeBase64);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return str;
	}

	/**
	 * md5加密
	 * @author jlchen4
	 * @date 2017年9月16日 下午3:44:46
	 * @param source	加密字符串
	 * @return
	 */
	public static String md5Encode(String source) {
		String result = null;
		try {
			result = source;
			// 获得MD5摘要对象
			MessageDigest messageDigest = MessageDigest.getInstance("MD5");
			// 使用指定的字节数组更新摘要信息
			messageDigest.update(result.getBytes("utf-8"));
			// messageDigest.digest()获得16位长度
			result = byteArrayToHexString(messageDigest.digest());
		} catch (Exception e) {
			logger.error("Md5 Exception!", e);
		}
		return result;
	}

	private static String byteArrayToHexString(byte[] bytes) {
		StringBuilder stringBuilder = new StringBuilder();
		for (byte tem : bytes) {
			stringBuilder.append(byteToHexString(tem));
		}
		return stringBuilder.toString();
	}

	private static String byteToHexString(byte b) {
		int n = b;
		if (n < 0) {
			n = 256 + n;
		}
		int d1 = n / 16;
		int d2 = n % 16;
		return hexDigits[d1] + hexDigits[d2];
	}
}
